package com.robotic.sensor.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

/**
 * Modbus RTU通信服务
 * 实现RS485串口通信和Modbus协议处理
 */
@Service
public class ModbusRTUService {
    
    private static final Logger log = LoggerFactory.getLogger(ModbusRTUService.class);
    
    // 通信参数（根据文档：波特率9600，数据位8，停止位1，校验位无）
    private static final int BAUD_RATE = 9600;
    private static final int DATA_BITS = 8;
    private static final int STOP_BITS = 1;
    private static final int PARITY = 0; // 无校验
    
    // 超时设置
    private static final int READ_TIMEOUT = 1000; // 1秒
    private static final int WRITE_TIMEOUT = 1000; // 1秒
    
    // 帧间隔（根据文档：一帧数据结束到下一帧开始的时间间隔至少2Byte的数据长度）
    private static final int FRAME_INTERVAL_MS = 20;
    
    private SerialPortWrapper serialPort;
    private boolean isConnected = false;
    
    /**
     * 连接串口
     * @param portName 串口名称（如COM1, COM2等）
     * @return 连接是否成功
     */
    public boolean connect(String portName) {
        try {
            log.info("正在连接串口: {}", portName);
            
            serialPort = new SerialPortWrapper();
            boolean connected = serialPort.open(portName, BAUD_RATE, DATA_BITS, STOP_BITS, PARITY);
            
            if (connected) {
                serialPort.setTimeouts(READ_TIMEOUT, WRITE_TIMEOUT);
                isConnected = true;
                log.info("串口连接成功: {}", portName);
            } else {
                log.error("串口连接失败: {}", portName);
            }
            
            return connected;
            
        } catch (Exception e) {
            log.error("连接串口异常: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 断开串口连接
     */
    public void disconnect() {
        try {
            if (serialPort != null && isConnected) {
                serialPort.close();
                isConnected = false;
                log.info("串口连接已断开");
            }
        } catch (Exception e) {
            log.error("断开串口连接异常: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 读取保持寄存器（功能码0x03）
     * @param deviceId 设备地址
     * @param startAddress 起始地址
     * @param quantity 寄存器数量
     * @return 读取的数据
     */
    public byte[] readHoldingRegisters(int deviceId, int startAddress, int quantity) {
        return readRegisters(deviceId, 0x03, startAddress, quantity);
    }
    
    /**
     * 读取输入寄存器（功能码0x04）
     * @param deviceId 设备地址
     * @param startAddress 起始地址
     * @param quantity 寄存器数量
     * @return 读取的数据
     */
    public byte[] readInputRegisters(int deviceId, int startAddress, int quantity) {
        return readRegisters(deviceId, 0x04, startAddress, quantity);
    }
    
    /**
     * 写单个寄存器（功能码0x06）
     * @param deviceId 设备地址
     * @param registerAddress 寄存器地址
     * @param value 写入值
     * @return 写入是否成功
     */
    public boolean writeSingleRegister(int deviceId, int registerAddress, int value) {
        try {
            if (!isConnected) {
                log.error("串口未连接");
                return false;
            }
            
            // 构建Modbus RTU请求帧
            byte[] request = new byte[8];
            request[0] = (byte) deviceId;                           // 设备地址
            request[1] = 0x06;                                      // 功能码
            request[2] = (byte) (registerAddress >> 8);             // 寄存器地址高字节
            request[3] = (byte) (registerAddress & 0xFF);           // 寄存器地址低字节
            request[4] = (byte) (value >> 8);                       // 数据高字节
            request[5] = (byte) (value & 0xFF);                     // 数据低字节
            
            // 计算CRC校验
            int crc = calculateCRC(request, 6);
            request[6] = (byte) (crc & 0xFF);                       // CRC低字节
            request[7] = (byte) (crc >> 8);                         // CRC高字节
            
            log.debug("发送写单个寄存器请求: {}", bytesToHex(request));
            
            // 发送请求
            serialPort.write(request);
            
            // 等待帧间隔
            Thread.sleep(FRAME_INTERVAL_MS);
            
            // 读取响应
            byte[] response = serialPort.read(8); // 写单个寄存器响应长度为8字节
            
            if (response != null && response.length == 8) {
                log.debug("接收到写单个寄存器响应: {}", bytesToHex(response));
                
                // 验证响应
                if (response[0] == (byte) deviceId && response[1] == 0x06) {
                    // 验证CRC
                    int receivedCRC = ((response[7] & 0xFF) << 8) | (response[6] & 0xFF);
                    int calculatedCRC = calculateCRC(response, 6);
                    
                    if (receivedCRC == calculatedCRC) {
                        log.debug("写单个寄存器成功");
                        return true;
                    } else {
                        log.error("写单个寄存器响应CRC校验失败");
                    }
                } else {
                    log.error("写单个寄存器响应格式错误");
                }
            } else {
                log.error("写单个寄存器无响应或响应长度错误");
            }
            
            return false;
            
        } catch (Exception e) {
            log.error("写单个寄存器异常: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 读取寄存器通用方法
     * @param deviceId 设备地址
     * @param functionCode 功能码
     * @param startAddress 起始地址
     * @param quantity 寄存器数量
     * @return 读取的数据
     */
    private byte[] readRegisters(int deviceId, int functionCode, int startAddress, int quantity) {
        try {
            if (!isConnected) {
                log.error("串口未连接");
                return null;
            }
            
            // 构建Modbus RTU请求帧
            byte[] request = new byte[8];
            request[0] = (byte) deviceId;                           // 设备地址
            request[1] = (byte) functionCode;                       // 功能码
            request[2] = (byte) (startAddress >> 8);                // 起始地址高字节
            request[3] = (byte) (startAddress & 0xFF);              // 起始地址低字节
            request[4] = (byte) (quantity >> 8);                    // 寄存器数量高字节
            request[5] = (byte) (quantity & 0xFF);                  // 寄存器数量低字节
            
            // 计算CRC校验
            int crc = calculateCRC(request, 6);
            request[6] = (byte) (crc & 0xFF);                       // CRC低字节
            request[7] = (byte) (crc >> 8);                         // CRC高字节
            
            log.debug("发送读取寄存器请求: {}", bytesToHex(request));
            
            // 发送请求
            serialPort.write(request);
            
            // 等待帧间隔
            Thread.sleep(FRAME_INTERVAL_MS);
            
            // 读取响应头（设备地址 + 功能码 + 数据长度）
            byte[] header = serialPort.read(3);
            
            if (header != null && header.length == 3) {
                int dataLength = header[2] & 0xFF;
                
                // 读取数据和CRC
                byte[] dataAndCrc = serialPort.read(dataLength + 2);
                
                if (dataAndCrc != null && dataAndCrc.length == dataLength + 2) {
                    // 组装完整响应
                    byte[] response = new byte[3 + dataLength + 2];
                    System.arraycopy(header, 0, response, 0, 3);
                    System.arraycopy(dataAndCrc, 0, response, 3, dataLength + 2);
                    
                    log.debug("接收到读取寄存器响应: {}", bytesToHex(response));
                    
                    // 验证响应
                    if (response[0] == (byte) deviceId && response[1] == (byte) functionCode) {
                        // 验证CRC
                        int receivedCRC = ((response[response.length - 1] & 0xFF) << 8) | (response[response.length - 2] & 0xFF);
                        int calculatedCRC = calculateCRC(response, response.length - 2);
                        
                        if (receivedCRC == calculatedCRC) {
                            // 提取数据部分
                            byte[] data = new byte[dataLength];
                            System.arraycopy(response, 3, data, 0, dataLength);
                            log.debug("读取寄存器成功，数据: {}", bytesToHex(data));
                            return data;
                        } else {
                            log.error("读取寄存器响应CRC校验失败");
                        }
                    } else {
                        log.error("读取寄存器响应格式错误");
                    }
                }
            }
            
            log.error("读取寄存器无响应或响应格式错误");
            return null;
            
        } catch (Exception e) {
            log.error("读取寄存器异常: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 计算CRC16校验码
     * @param data 数据
     * @param length 数据长度
     * @return CRC16校验码
     */
    private int calculateCRC(byte[] data, int length) {
        int crc = 0xFFFF;
        
        for (int i = 0; i < length; i++) {
            crc ^= (data[i] & 0xFF);
            
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        
        return crc;
    }
    
    /**
     * 字节数组转十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }
    
    /**
     * 检查连接状态
     * @return 是否已连接
     */
    public boolean isConnected() {
        return isConnected;
    }
    
    /**
     * 串口包装类（简化实现，实际项目中需要使用具体的串口库）
     */
    private static class SerialPortWrapper {
        private OutputStream outputStream;
        private InputStream inputStream;
        
        public boolean open(String portName, int baudRate, int dataBits, int stopBits, int parity) {
            // 这里需要实现具体的串口打开逻辑
            // 可以使用RXTX、jSerialComm或其他串口库
            log.warn("串口打开功能需要集成具体的串口库实现");
            return false; // 暂时返回false，需要实际串口库支持
        }
        
        public void setTimeouts(int readTimeout, int writeTimeout) {
            // 设置超时
        }
        
        public void write(byte[] data) throws IOException {
            if (outputStream != null) {
                outputStream.write(data);
                outputStream.flush();
            }
        }
        
        public byte[] read(int expectedLength) throws IOException {
            if (inputStream != null) {
                byte[] buffer = new byte[expectedLength];
                int bytesRead = inputStream.read(buffer);
                if (bytesRead > 0) {
                    byte[] result = new byte[bytesRead];
                    System.arraycopy(buffer, 0, result, 0, bytesRead);
                    return result;
                }
            }
            return null;
        }
        
        public void close() {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error("关闭串口异常: {}", e.getMessage());
            }
        }
    }
}